import {isDate, isPlainObject, isVoidValue} from './util'
import {flowRight} from 'lodash'

type urlType = number | string | Array<any> | object | Date;

function encode (val: string): string {
  return encodeURIComponent(val)
  .replace(/%40/g, '@')
  .replace(/%3A/gi, ':')
  .replace(/%24/g, '$')
  .replace(/%2C/gi, ',')
  .replace(/%20/g, '+')
  .replace(/%5B/gi, '[')
  .replace(/%5D/gi, ']');
}

export function buildURL (url: string, params?: any) {
  if (!params) {
    return url
  }

  let parts: string[] = [];

  getObjKey(params).forEach((key) => {
    let val = params[key];

    if (isVoidValue(val)) {
      return;
    }

    const keyname = Array.isArray(val) ? `${key}[]` : key;
    const mapParams = flowRight(mapValue, arrayConvert);
    const strParams = mapParams(val);
    parts.push(...mapEncoded(strParams, keyname));
  })

  return serializeUrl(url, parts);
}

function getObjKey (params: Object): string[] {
  return Object.keys(params);
}

function arrayConvert (val: urlType): Array<urlType> {
  return Array.isArray(val) ? val : [val];
}

function mapValue (val: Array<urlType>): Array<string> {
  const result = val.map(item => {
    if (isDate(item)) {
      return item.toISOString();
    } else if (isPlainObject(item)) {
      return JSON.stringify(item);
    } else {
      return item!.toString()
    }
  })

  return result;
}

function mapEncoded (params: Array<string>, key: string): Array<string> {
  return params.map(item => {
    return `${encode(key)}=${encode(item)}`;
  });
}

function serializeUrl (url: string, paramsArr: string[]): string {
  let serializedParams = paramsArr.join('&');

  if (serializedParams) {
    const markIndex = url.indexOf('#')
    if (markIndex !== -1) {
      url = url.slice(0, markIndex)
    }
    url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;
  }

  return url;
}